package cn.xxm.dongbao.user.secure.dos;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * It depend on HTTP protocol session {@link HttpSession}. After user login the site, the user will renew the session * because of the security rule, the old statistics will lost that it is know issue.
 */
public class HttpSessionDosStatisticsRepository implements DosStatisticsRepository {
    private static final Logger LOG = LoggerFactory.getLogger(HttpSessionDosStatisticsRepository.class);
    private static final String DOS_STATISTICS_SESSION_KEY = "DOS_STATISTICS_SESSION_KEY";
    private final Object lock = new Object();

    /**
     * The downcast from object to tokenBucket     *     * @param <T> generics type     * @param bucket the parameter to downcast     * @return token bucket
     */
    public static <T> TokenBucket getTokenBucket(T bucket) {
        TokenBucket tokenbucket = null;
        if (bucket instanceof TokenBucket) {
            tokenbucket = (TokenBucket) bucket;
        }
        return tokenbucket;
    }

    @Override
    public TokenBucket getStatistics(HttpServletRequest request, DosConfig config) throws DosStatisticsException {
        HttpSession session = request.getSession(true);
        TokenBucket bucket = getTokenBucket(session.getAttribute(DOS_STATISTICS_SESSION_KEY));
        if (bucket == null) {
            synchronized (lock) {
                // Strict make the thread safe.
                bucket = getTokenBucket(session.getAttribute(DOS_STATISTICS_SESSION_KEY));
                if (bucket == null) {
                    bucket = new TokenBucket(config);
                    // Save the statistics into the session directly to avoid
                    // the locker in the saveStatistics method.
                    session.setAttribute(DOS_STATISTICS_SESSION_KEY, bucket);
                }
            }
        }
        return bucket;
    }

    /**
     * Not create the new session, and remove the statistic if it can be reachable.
     * Actually the session will be invalid, the statistic object also will
     * be die that don't need remove by manual.
     * @param request HttpServletRequest
     * @param bucket Token bucket
     * @throws DosStatisticsException when remove TokenBucket failed
     */
    @Override
    public void removeStatistics(HttpServletRequest request, TokenBucket bucket) throws DosStatisticsException {
        HttpSession session = request.getSession(false);
        if (session != null) {
            if (session.getAttribute(DOS_STATISTICS_SESSION_KEY) != null) {
                session.removeAttribute(DOS_STATISTICS_SESSION_KEY);
            } else {
                LOG.error("Fail to remove TokenBucket, because TokenBucket object dose not exist.");
                throw new DosStatisticsException("Fail to remove TokenBucket.");
            }
        } else {
            LOG.error("Fail to remove TokenBucket, because session and TokenBucket object do not exist.");
            throw new DosStatisticsException("Fail to remove TokenBucket.");
        }
    }

    /**
     * While get the statistics from session that has been saved.
     * @param request HttpServletRequest
     * @param bucket Token bucket
     * @throws DosStatisticsException DosStatisticsException
     */
    @Override
    public void saveStatistics(HttpServletRequest request, TokenBucket bucket) throws DosStatisticsException {
    }
}